Принципы объектно-ориентированного программирования

         

Создание графических пользовательских интерфейсов

Ксожалению, конструктор форм (Forms Designer) не поддерживается в C++ Тем не менее, вы можете использовать конструктор форм (Forms Designer) в С#, и потом перенести полученный с помощью С# код графического интерфейса пользователя в программу на C++ Для переноса графического интерфейса пользователя в программу на C++ необходимы дополнительные усилия, и, в большинстве случаев, такой перенос особой пользы не дает Как правило, C++ не используют для разработки пользовательских графических интерфейсов, вместо этого применяется подход смешения языков, в котором для создания почьзовательского интерфейса используется С#, а в других аспектах разработки проекта из разных соображений используется C++ Поскольку вам, скорее всего, придется часто создавать графический интерфейс


пользователя для многих приложений, в этой паве мы преследуем две цели Во-первых, мы хотим научить вас совместно использовать код на С# и C++ Даже если вы в основном программируете на C++, стоит ознакомиться с С# и конструктором форм (Forms Designer), — у вас появится возможность использовать те мощные инструментальные программные средства, которые не поддерживаются в C++ Во-вторых, мы приведем в пример один из немногих случаев, когда перенос кода графического интерфейса пользователя из С# в программу на C++ является целесообразным В главе представлено несколько пар примеров кода графических пользовательских интерфейсов до и после переноса
Ключевым средством взаимодействия пользователя с компьютером является графический пользовательский интерфейс (Graphical User Interface, GUI) Из этой главы вы узнаете, как создавать графический пользовательский интерфейс с помощью классов Windows Forms (Формы Windows), которые находятся в NET Framework На практике программирование Windows-приложений предполагает экстенсивное использование различных инструментальных средств и мастеров, которые намного упрощают этот процесс Однако все указанные средства автоматизации заслоняют то, что лежит в основе создания графического пользовательского интерфейса Поэтому сначала мы рассмотрим основы создания графических пользовательских интерфейсов Иными словами, мы научимся создавать простые приложения Windows с самого начала, пользуясь только комплексом инструментальных средств разработки программ NET Framework SDK Это значит, что вначале мы будем создавать простые приложения Windows без применения каких-либо специальных сервисных программ Будут рассмотрены основы рисования с помощью Windows Forms (Формы Windows) с применением шрифтов и кистей, а также необходимые обработчики событий Мы объясним принципы обработки событий в Windows Forms (Формы Windows) и реализуем обработчики событий мыши. С помощью Windows Forms (Формы Windows) мы также реализуем меню и соответствующие обработчики событий. Кроме того, мы рассмотрим управляющие элементы, а после этого изучим среду Visual Studio.NET, посредством которой можно без труда создать простой графический пользовательский интерфейс на С#. С помощью конструктора форм (Forms Designer) добавим в форму управляющие элементы, создадим меню, добавим обработчики событий и другие полезные функциональные возможности. При желании полученный в результате проект на С# можно потом перенести на C++. В заключение будут рассмотрены диалоговые окна и такой элемент управления, как список.

Иерархия Windows Forms (Формы Windows)

Windows Forms (Формы Windows) — это та часть каркаса .NET Framework, которая поддерживает создание приложений со стандартным графическим пользовательским интерфейсом (GUI) на платформе Windows. Среди классов Windows Forms (Формы Windows) есть обширный набор классов для создания сложных графических пользовательских интерфейсов. Эти классы можно использовать в приложениях, написанных на любом языке .NET



Рис. 6.1. Упрощенная схема иерархии классов Windows Forms (Формы Windows)

Как правило, ваше приложение будет содержать главное окно, которое реализовано с помощью некоторого класса MyForm, производного от класса Form (Форма). На рис 6.1 изображено, какое место ваш класс MyForm занимает в иерархии классов Windows Forms (Формы Windows).



Создание простых форм с помощью комплекса инструментальных средств разработки программ .NET SDK

Для ознакомления с классами Windows Forms (Формы Windows) полезно будет создать простое приложение SimpleForm (Простая форма) в несколько шагов. Ни на одном из этих шагов мы не будем использовать средства проектирования Visual Studio. Используя интерфейс командной строки, необходимо запустить командный файл build. bat.



Создание простой формы

Приложение SimpleForm (Простая форма) — скелет стандартного приложения Windows. Вот код приложения SimpleForm (Простая форма), созданный на шаге 0:

//SimpleForm.срр - Шаг 0
// Эта версия отображает простую форму (simple form)
fusing <mscorlib.dll>
fusing <System.dll>
fusing <System.Drawing.dll> // Система
fusing <System.Windows.Forms.dll> // Система
using namespace System;
// использование пространства имен Система;
using namespace System::Windows::Forms;
// использование пространства имен Система::Windows::Формы;
_gc class Forml : public Form
// класс сборщика мусора Forml: общедоступная Форма
{
public:
Forml()
{
Size = // Размер
*_nogc new System::Drawing::Size(300,200); // Размер
Text = "Simple Form - Step 0";
// Текст = "Простая Форма - Шаг О";
}
static void Main()
{
Application::Run(new Forml);
// Приложение:: Выполнить (новая Forml);
}
};
int _stdcall WinMain(
long hlnstance, // дескриптор текущего экземпляра
long hPrevInstance, // дескриптор предыдущего экземпляра
long IpCmdLine, // командная строка
int nCmdShow // состояние отображения )
{
Forml::Main();
return 0;
}

Класс Forml является производным от System:: Windows:: Forms:: Form (Cистема::Windows::Формы::Форма). В классе System::Windows::Forms::Application (Система::Windows::Формы::Приложение) есть статические методы для управления приложением, например Run (Выполнить) и Exit (Выход). Метод WinMain создает новую форму и запускает ее в качестве главного окна приложения.
Обратите внимание, что в примерах этой главы, написанных на C++, вместо имени функции main (главная) в качестве точки входа используется WinMain. В принципе можно в функции main (главная) в рамках консольного приложения реализовать все возможности графического интерфейса пользователя. Но при этом подходе придется создать бездействующее консольное окно, что в приложениях с графическим пользовательским интерфейсом совсем ни к чему. Если же использовать WinMain вместо main (главная), то в программе не создаются консольные окна, а сразу создается главное окно.
Конструктор формы инициализирует форму. Значение в поле Size (Размер) определяет размер формы в пикселях. Поле Text (Текст) определяет заголовок, который отображается в области заголовка окна новой формы.
Ключевым классом Windows Forms (Формы Windows) является базовый класс Form (Форма). Этот класс содержит обширный набор функций, которые наследуются разрабатываемыми нами классами форм, производными от класса Form (Форма).
Чтобы создать приложение, нужно выполнить из командной строки командный файл build.bat. А чтобы запустить командный файл, откройте окно DOS, перейдите в папку SimpleFormXStep(), и введите в командной строке build (компоновка). Помните, что . перед этим необходимо правильно установить значения переменных среды. Для этого достаточно выполнить Visual Studio.NET Command Prompt.

cl /CLR SimpleForm.cpp

По умолчанию будет откомпилирован исполняемый файл Windows. В исходном коде приложения находятся директивы fusing, в которых указаны используемые библиотеки .NET: System.dll,System.Drawing.dllи System.Windows.Forms.dll.
После того, как вы откомпилировали приложение с помощью командного файла, можете запустить его, введя в командной строке SimpleForm (Простая форма). Вы также можете запустить приложение в проводнике Windows, дважды щелкнув на файле SimpleForm.exe. На рис. 6.2 изображен внешний вид этого простого приложения. И хотя приложение SimpleForm (Простая форма) совсем примитивное, в нем заложено множество возможностей, унаследованных созданным нами классом, который является производным от класса Form (Форма). Окно приложения можно перетаскивать по экрану, изменять его размер, свертывать, развертывать, в нем можно открывать системное меню (щелкнув кнопкой мыши в верхнем левом углу окна) и т.д.

Сообщения о работе окна

В Visual Studio.NET есть инструментальное средство под названием Spy++ (Шпион++). Эта программа "шпионит" за окнами, чтобы иметь представление о том, что происходит внутри окна. Чтобы в Visual Studio запустить Spy++ (Шпион++), нужно воспользоваться меню Tools (Сервис). Запустите версию приложения SimpxeForm.exe, полученную на шаге 0, а затем запустите Spy++ (Шпион++). Выберите Spy (Шпион) Find Window (Найти окно) — появится диалоговое окно Find Window (Найти окно). В этом диалоговом окне установите переключатель Messages (Сообщения), как на рис. 6.3



Рис. 6.2. Скелет приложения \Vindowb Forms (Формы Windows) (Шаг 0)



Рис. 6.3. Инструмент Finder Tool (Средство поиска) служит для поиска окна — объекта шпионажа

Левой кнопкой мыши перетащите инструмент Finder Tool (Средство поиска) (в диалоговом окне Find window (Найти окно) этот инструмент отображается в виде специальной пиктограммы — перекрестия) на окно приложения SimpleForm (Простая форма), а потом щелкните на кнопке ОК. Теперь в окно программы-шпиона Spy++ будут выводиться сообщения, информирующие обо всех взаимодействиях с окном. Окно программы-шпиона Spy++ показано на рис. 6.4.
Чтобы обрабатывать события, приложения Windows должны иметь специальную структуру Операционная система Windows в ответ на действие пользователя, например щелчок кнопкой мыши, выбор меню или ввод символов с клавиатуры, посылает приложению сообщение Приложения Windows должны иметь такую структуру, которая позволяет реагировать на эти сообщения
Удобство создания Windows-программ с помощью классов NET Framework состоит в том, что программировать можно на очень высоком уровне абстракции На шаге 0 вы уже убедились, насколько просто создать приложение В последующих разделах мы будем добавлять в приложение новые основные свойства графических пользовательских интерфейсов, и таким образом проиллюстрируем основы создания графических пользовательских интерфейсов с помощью классов Windows Forms (Формы Windows)



Отображение текста на форме

В приложении, созданном на первом шаге, будет показано, как отобразить на форме текст На рис. 6.5. можно увидеть, как выглядит это приложение при выполнении



Рис. 6.4. Окно Messages (Сообщения) программы Spy++(Шпион++)



Рис. 6.5. Отображение текста на простой форме (Шаг 1)

Вывод данных в программах Windows сильно отличается от вывода данных в аналогичных консольных приложениях, где для этого используется метод Console: :WriteLine Вычерчивание результата в окне часто называют закрашиванием или закраской Закрашивание выполняется в ответ на сообщение "раскрасить", WM_PAINT Такой способ закрашивания по требованию гарантирует, что если окно будет накрыто каким-либо другим окном, а затем открыто снова, то содержимое окна будет отображено корректно
Еще одно отличие выполнения вывода в Windows-программах от выполнения вывода в консольных приложениях состоит в том, что необходимо определить все детали Например, нужно указать координаты области рисования, "кисть", которой будет выполняться рисование, шрифт текста, и так далее Вот код приложения, созданного на шаге 1

//SimpleForm.cpp - Шаг 1
// Эта версия отображает приветствие
fusing <mscorlib.dll>
#using <System.dll>
#using <System.Drawing.dll>
#using <System.Windows.Forms.dll>
using namespace System;
// использование пространства имен Система;
using namespace System::Windows:.Forms;
// использование пространства имен Система.:Windows::Формы;
using namespace System::Drawing;
// использование пространства имен Система::Рисование;
_gc class Forml : public Form
// класс сборщика мусора Forml: общедоступная Форма
{
private: // частный
float x, у; // с плавающей точкой Brush *pSto<Brush; // Кисть public:
Forml() {
Size = // Размер
*_nogc new System::Drawing::Size(300,200);
// Размер
Text = "Simple Form - Step 1";
// Текст = "Простая Форма - Шаг 1";
x = у = 10; pStdBrush = new SolidBrush(Color::Black);
// Красить::
// Черным } protected: // защищенный
virtual void OnPaint(PaintEventArgs * ppea)
{
ppea->get_Graphics()->Graphics::DrawString // Графика
("Hello, Window Forms", Font,
// "Привет, Формы Window ", Шрифт,
pStdBrush, x, у);
}
public:
static void Main() {
Application.:Ran(new Forml);
// Приложение:: Выполнить (новая Форма),
}
};
int _stdcall WinMain(
long hlnstance, // дескриптор текущего экземпляра
long hPrevInstance, // дескриптор предыдущего экземпляра
long IpCmdLine, // командная строка
int nCmdShow // состояние отображения )
{
Forml::Main();
return 0;
}

Для того чтобы рисовать с помощью Windows Forms (Формы Windows), нужно переопределить виртуальный метод OnPaint Класс PaintEventArgs содержит объект Graphics в качестве свойства, доступного только для чтения Класс Graphics, который принадлежит пространству имен System: -Drawing (Система Рисунок), содержит методы рисования
Параметры метода Drawstring

выводимая строка, шрифт (Font (Шрифт) — свойство класса Form (Форма), которое определяет шрифт, по умолчанию применяемый для вывода текста в форму), используемая кисть, координаты в пикселях (числа типа float (с плавающей точкой))

В качестве стандартной кисти используется черная кисть SolidBrush



Обработка событий в Windows Forms (Формы Windows)

Графический пользовательский интерфейс (GUI) управляется событиями приложение выполняет действия в ответ на события, вызванные пользователем, например, на щелчок кнопкой мыши или выбор пункта меню Каждая форма или элемент управления имеет заранее определенный набор событий Например, у каждой формы есть код, обрабатывающий событие MouseDown (Кнопка мыши нажата)
В Windows Forms (Формы Windows) применяется модель обработки событий NET, в которой делегаты используются для того, чтобы связать события с обрабатывающими их методами В классах Windows Forms (Формы Windows) используются групповые делегаты Групповой делегат содержит список связанных с ним методов Когда в приложении происходит событие, управляющий элемент возбуждает событие, вызвав делегат для этого события Потом делегат вызывает связанные с ним методы
Для того чтобы добанить де iciar к событию, в C++ используется перегруженный оператор += Мы добавляем метод Forml_MouseDowr к событию MouseDown (Кнопка мыши нажата)

MouseDown += new MouseEventHandler
(this, Forml_MoaseDown),

Вскоре мы увидим этот код в программе



Документация по обработке событий

Документацию, касающуюся событий и их обработки, можно найти в справочнике по NET Framework ( NET Framework Reference) На рис 6 6 показаны предопредепенные события, связанные с классом Form (Форма)



Событие MouseDown (Кнопка мыши нажата)

Событие MouseDown (Кнопка мыши нажата) является одним из предопределенных событий класса Control (Элемент управления), от которого порожден класс Form (Форма)

public: _event MouseEventHandler* MouseDown;

А вот и объявление обработчика этого события, MouseEventHandler

public _gc _delegate void MouseEventHandler(
Object* sender, // отправитель
MouseEventArgs* e
) ;



Рис. 6.6. Документация по событиям класса Form (Форма)

В качестве параметра обработчик событий получает объект класса Мои seEventArgs (производный от класса EventArgs) Свойства этого объекта доступны только для чтения и содержат информацию, связанную с данным событием

Button (Кнопка) определяет, какая кнопка была нажата, Clicks (Щелчки) определяет, сколько раз была нажата и отпущена кнопка, Свойство Delta (Дельта) является счетчиком оборотов колесика мыши; X и Y — координаты точки, в которой находился указатель в момент нажатия кнопки мыши

Обработка событий мыши

На шаге 2 мы внесем изменения в приложение, чтобы при щелчке любой кнопкой мыши строка с приветствием перемещалась на место щелчка На рис. 6.7 можно увидеть, что после щелчка кнопкой мыши строка действительно переместилась



Рис. 6.7. Перемещение текста по щелчку кнопкой мыши (Шаг 2)

//SimpleForm.cpp - Шаг 2
// SimpleForm.срр - 2
// Эта версия отображает приветствие, которое может быть перемещено
// щелчком кнопки мыши
fusing <mscorlib.dll>
fusing <System.dll>
fusing <System.Drawing dll>
fusing <System.Windows.Forms dll>
using namespace System;
// использование пространства имен Система;
using namespace System::Windows. Forms;
// использование пространства имен Система::Windows::Формы;
using namespace System::Drawing;
// использование пространства имен Система:: Рисунок;
_go class Forml : public Form
// класс сборщика мусора Forml: общедоступная Форма
{
private: // частный
void InitializeComponent()
{
MouseDown += new MouseEventHandler // новый обработчик
// события
(this, Forml_MouseDown);
}
float x, у; // с плавающей точкой
Brush *pStdBrush; // Кисть public:
Forml()
{
InitializeComponent(); Size = // Размер
*_nogc new System::Drawing::Size(300,200); // Размер
Text = "Simple Form - Step 2"; // Текст = "Простая Форма - Шаг 2"; x = у = 10;
pStdBrush = new SolidBrush(Color::Black) ; // Красить::Черным
}
protected- // защищенный void Forml_MouseDown
(Object *pSender, MouseEventArgs *pmea)
{
x = pmea->X; у = pmea->Y;
Invalidate(); I
}

Во время инициализации программа связывает метод Forml_MouseDown с событием MouseDown (Кнопка мыши нажата) Этот метод устанавливает координаты текста, х и у, равными координатам точки, в которой находился указатель в момент щелчка Для того чтобы понять роль метода Invalidate (Считать недействительным), закомментируйте его и снова скомпонуйте код Щелкните мышью, чтобы перенести текст Что произойдет в результате9 Приветствие останется на том же месте Потом накройте окно приложения каким-либо другим окном, а потом снова откройте его Теперь вы увидите, что строка перемещена
Метод Invalidate (Считать недействительным) определен в базовом классе Control (Элемент управления) Существует несколько переопределенных версий этого метода Каждая из них заставляет считать недействительной определенную область управляющего элемента и посылает элементу управления сообщение о перерисовывании Метод, который не содержит параметров, заставляет считать недействительным весь управляющий элемент Для того чтобы максимально уменьшить объем перерисовывания, в более сложном приложении можно ограничиться тем, что недействительным будет считаться прямоугольник



М: Несколько обработчиков для события

На шаге 2М создания нашего приложения мы реализуем два разных обработчика события MouseDown (Кнопка мыши нажата). Второй обработчик по щелчку кнопкой мыши просто отображает окно сообщения.

//SimpleForm.срр - Шаг 2М
// Эта версия имеет два обработчика событий для MouseDown
_gc class Forml : public Form
// класс сборщика мусора Forml: общедоступная Форма
{
private: // частный
void InitializeComponent()
{
MouseDown += new MouseEventHandler
(this, Forml__MouseDown) ;
MouseDown += new MouseEventHandler
(this, ShowClick);
}
void Forml_MouseDown
(Object *pSender, MouseEventArgs *pmea)
{
x = pmea->X;
у = pmea->Y;
Invalidate(); }
void ShowClick (Object *pSender, MouseEventArgs *pmea)
{
MessageBox::Show("Mouse clicked!!'");
// "Мышь щелкнула!!! }



События MouseDown (Кнопка мыши нажата) и Keypress (Нажатие клавиши)

На шаге 3 мы введем в наш пример обработку еще одного события, а именно, события KeyPress (Нажатие клавиши), а также покажем, как в событии MouseDown (Кнопка мыши нажата) различать, какая кнопка была нажата, левая или правая.

Обработка событий, вызванных правой и левой кнопкой мыши

Для того чтобы определить, какая кнопка мыши была нажата, нужно использовать свойство Button (Кнопка) параметра MouseEventArgs. Правую кнопку мыши будем использовать для удаления строки с приветствием, которая хранится в элементе данных str класса StringBuilder.

void Forml_MouseDown
(Object *pSender, MouseEventArgs *pmea)
{
if (pmea->Button == МоиseButtons::Left) // если левая кнопка
{
x = pmea->X;
у = pmea->Y;
}
else if (pmea->Button == MouseButtons::Right) // если правая
// кнопка
{
pStr = new StringBuilder();
}
Invalidate() ;
}

Событие Keypress (Нажатие клавиши)

На шаге 3 мы научимся обрабатывать событие KeyPress (Нажатие клавиши). Каждый раз, когда пользователь нажмет клавишу, в конец строки приветствия будет добавлен соответствующий символ. Обратите внимание, что вместо класса String (Строка) используется класс StringBuilder, который более эффективен в этой ситуации. Объект String (Строка) — стационарный (неизменяемый), то есть, для того, чтобы реализовать добавление символов в конец строки, нужно постоянно удалять одни объекты String (Строка) и создавать другие.

StringBuilder *pStr;
void Forml_KeyPress
(Object *pSender, KeyPressEventArgs *pmea)
{
pStr—>Append(pmea->KeyChar) ; // Добавляем в конец
Invalidate() ;
}

Также, как и на шаге 2, необходимо вызвать метод Invalidate (Считать недействительным), для того, чтобы принудительно перерисовать окно приложения после сделанных изменений. На рис. 6.8 показано окно приложения SimpleForm (Простая форма), после удаления исходного текста и ввода нового.



Рис. 6.8. Испытываем события мыши и нажатия клавиши на клавиатуре (Шаг 3)



Меню

Все пользователи Windows-приложений хорошо знакомы с меню, которые представляют собой простой механизм выбора команд. В языках .NET меню реализуется в самой программе. Иными словами, для меню файл ресурсов не нужен.



Меню для выхода из программы

На шаге 4 мы добавим в наше приложение Simple Form простое меню Для того чтобы выйти из программы, пользователь должен выбрать File => Exit (Файл => Выход), как на рис. 6.9.



Рис. 6.9. Шаг 4 Добавление в форму меню File => Exit (Файл => Выход)



Код меню

//SimpleForm.срр - Шаг 4
_gc class Forml : public Form
// класс сборщика мусора Forml: общедоступная Форма
{
private: // частный
void InitializeComponent()
{
pMainMenul = new MainMenu ();
pMenuFile = new Menultem ();
pMenuExit = new Menultem ();
// mainMenul
Menultem* pMainMenulItems[] = {pMenuFile};
pMainMenul->get_MenuItems()
->AddRange(pMainMenulItems); // Меню File
pMenuFile->set_Index(0);
Menultem* pMainFileltems[] = {pMenuExit};
pMenuFile->get_MenuItems()
->AddRange(pMainFileltems);
pMenuFile->set_Text("File"); // Файл
// Меню Exit
pMenuExit->set_Index{0);
pMenuExit->set_Text("Exit"); // Выход
pMenuExit->Click += new System::EventHandler // Щелчок (this, MenuExit_Click);
Menu = pMainMenul; // Меню
MouseDown += new MouseEventHandler
(this, Forml_MouseDown);
KeyPress += new KeyPressEventHandler
(this, Forml_KeyPress); }
float x, у; // с плавающей точкой
Brush *pStdBrush; // Кисть
StringBuilder *pStr;
Menultem *pMenuExit;
Menultem *pMenuFile;
MainMenu *pMainMenul;
public:
private: // частный
void MenuExit_Cliok(
Object *pSender, EventArgs *pea)
{
Application::Exit(); // Приложение:: Выход
}

В методе initializeComponent создается иерархическая структура меню, представленная экземпляром класса MainMenu (Главное меню). Меню состоит из объектов Menultem, каждый из которых является отдельной командой меню Каждый объект Menultem является командой приложения или командой родительского меню для других пунктов подменю В нашем приложении мы связываем объект MainMenu (Главное меню) с объектом Form (Форма), присваивая свойству Menu (Меню) объекта Form (Форма) значение MainMenu (Главное меню)
Когда в этой главе мы позже обсудим конструктор форм (Forms Designer), вы увидите, что меню можно создать и так нужно просто перетянуть элемент управления MainMenu (Главное меню) с панели инструментов на форму. Конструктор форм (Forms Designer) позаботится о генерации нужного шаблонного кода.



Код события Menu (Меню)

Как и в случае других событий Windows Forms (Формы Windows), с событием связывается его делегат Щелчок на пункте меню приводит к выполнению соответствующей команды

void InitializeComponent()
{
pMenuExit->Click += new System::EventHandler // Щелчок
(this, MenuExit_Click);
}
void MenuExit_Click(
Object *pSender, EventArgs *pea)
{
Application::Exit(); // Приложение::Выход
}



Управляющие элементы

В программе, которую мы только что рассматривали, объект pMainMenul является управляющим элементом. Данный объект — указатель на экземпляр класса MainMenu (Главное меню). Управляющий элемент — это объект на форме, который придает форме новые функциональные возможности. Управляющий элемент автоматически выполняет много заданий "по поручению" формы. Использование управляющих элементов упрощает программирование, так как программисту не надо беспокоиться о рисовании, о том, следует ли считать представление формы недействительным, не нужно думать о графических элементах и так далее. Для того чтобы реализовать простое меню с самого начала, нам пришлось написать довольно много кода. Управляющие элементы, которые реализуются с помощью объектов, содержат богатый, вполне пригодный для повторного использования код.



Использование управляющего элемента TextBox (Поле)

На шаге 5 создания приложения SimpleForm (Простая форма) мы используем управляющий элемент TextBox (Поле) для отображения строки с приветствием. В более ранних версиях приложения строку можно было переместить щелчком левой кнопки мыши и удалить щелчком правой кнопки мыши. Можно было также ввести свою собственную строку с приветствием. Теперь, применив управляющий элемент TextBox (Поле), вы получите полноценные возможности редактирования. Управляющий элемент TextBox (Поле) позволяет в любом месте строки вставить символы, вырезать и вставить текст (с помощью комбинаций клавиш Ctrl+X и Ctrl+V соответственно) и так далее. Все возможности редактирования поддерживаются управляющим элементом TextBox (Поле). На рис. 6.10 изображено окно приложения после того, как текст приветствия был перемещен, и мы ввели некий собственный текст.
Это новая версия программы. Обратите внимание на то, что она значительно проще предыдущей, хотя и имеет гораздо более богатые функциональные возможности. Нет больше необходимости использовать переменные экземпляра для координат и текста строки приветствия (теперь эта информация хранится в управляющем элементе pTxtGreeting типа TextBox (Поле)). Не нужен больше метод OnPaint, так как управляющий элемент TextBox (Поле) знает, как нарисовать себя. Можно также избавиться от кисти. Теперь не нужно обрабатывать событие KeyPress (Нажатие клавиши), потому что оно автоматически обрабатывается управляющим элементом TextBox (Поле), притом весьма аккуратно.



Рис. 6.10. Шаг 4: текст приветствия отображается с помощью управляющего элемента Text Box (Поле)

//SimpleForm.cpp - Шаг 5
_gc class Forml : public Form
// класс сборщика мусора Forml: общедоступная Форма
{
private: // частный
void InitializeComponent()
{
// текст приветствия (text greeting) pTxtGreeting = new TextBox; pTxtGreeting->Location = // Местоположение
* _nogc new Point(10, 10); // новая точка pTxtGreeting->Size = // Размер
(*_nogc new struct Size(150, 20)); // новый Размер pTxtGreeting->Text = "Hello, Windows Forms"; // Текст = "Привет, Формы Windows";
Controls->Add(pTxtGreeting); // Добавить
}
float x, у; // с плавающей точкой
Brush *pStdBrush; // Кисть
Menultem *pMenuExit;
Menultem *pMenuFile;
MainMenu *pMainMenul;
TextBox *pTxtGreeting;
protected: // защищенный void Forml_MouseDown
(Object *pSender, MouseEventArgs *pmea) {
if (pmea->Button == MouseButtons::Left)
// если кнопка левая
{
pTxtGreeting->Location = // Местоположение
*_nogc new Point(pmea->X, pmea->Y); // новая точка }
else if (pmea->Button == MouseButtons::Right) // если кнопка правая {
pTxtGreeting->Text = ""; // Текст } }
};

Управляющий элемент TextBox (Поле) удобен в использовании. В инициализирующей части программы мы создаем объект TextBox (Поле) и определяем значения его свойств Location (Местоположение), Size (Размер) и Text (Текст). Мы добавляем новый управляющий элемент к коллекции управляющих элементов Controls (Управляющие элементы) этой формы. В обработчике событий мыши мы перемещаем управляющий элемент, изменив значение свойства Location (Местоположение). С помощью свойства Text (Текст) управляющего элемента TextBox (Поле) можно удалить строку с приветствием.



Visual Studio.NET и формы

И хотя вполне реально создать приложение Windows Forms (Формы Windows), используя в командной строке только комплекс инструментальных средств разработки программ .NET Framework SDK, на практике подобную работу намного проще выполнить с помощью Visual Studio.NET. К сожалению, в Visual Studio.NET нет средств для генерирования проекта пусковой системы на управляемом C++ на основе Form (Форма), и управляемый C++ не поддерживает конструктор форм (Forms Designer). Однако для начала можно создать проект Windows-приложения на С# (Windows Application). При этом будет сгенерирован код пусковой системы и будут установлены ссылки на необходимые библиотеки .NET. Затем можно в конструкторе форм (Forms Designer) перетащить управляющие элементы с инструментальной панели на вашу форму. Конструктор форм (Forms Designer) вставит необходимый шаблон кода на С#, который поддерживает функционирование этих управляющих элементов в форме. В окне Properties (Свойства) несложно определить свойства управляющего элемента в процессе проектирования. Можно, конечно, определить эти свойства и во время запуска приложения, как мы это сделали для поля pTxtGreeting в предыдущем примере. После этого можно перенести код С# в программу на управляемом C++, но этого обычно не рекомендуется делать.



Демонстрация Windows Forms (Формы Windows)

Лучший способ научиться создавать приложения Windows с помощью Visual Studio.NET — самостоятельно с самого начала создать небольшое приложение на С#. Для примера мы создадим Windows-приложение, которое позволит вносить деньги на счет и снимать деньги со счета в банке.
1. Создайте на СП новый проект Windows Application (Windows-приложение), как на рис. 6.11, и назовите его BankGui.



Рис. 6.11. Создание проекта Windows Application (Windows-приложение)

2. Раскройте панель инструментов Toolbox, перетянув указатель мыши на вертикальную вкладку Toolbox в левой части главного окна Visual Studio. Если вкладки нет, инструментальную панель Toolbox можно открыть из меню ViewOToolbox (Вид1^ Панель инструментов). Чтобы панель инструментов Toolbox оставалась открытой, щелкните на "канцелярской кнопке", которая находится в заголовке панели инструментов Toolbox рядом с X. Если курсор мыши навести на "канцелярскую кнопку", появится подсказка с надписью "Auto Hide" (Автоматическое свертывание).
3. Перетащите из панели инструментов Toolbox две надписи (Label), два поля (TextBox) и две кнопки (Button) на форму (рис. 6.12).



Рис. 6.12. Перетаскивание управляющих элементов с панели инструментов Toolbox() на форму

4. В конструкторе форм (Forms Designer) щелкните на надписи labell. Тем самым вы выделите этот управляющий элемент в окне Properties (Свойства), которое находится под Solution Explorer (Поиск решения). Окно Properties (Свойства) позволяет изменять свойства управляющих элементов. В поле свойства Text (Текст) объекта labell введите Amount (Сумма). После того, как вы ввели значение, нажмите возврат каретки. Вы увидите, что текст появится на форме. На рис. 6.13 показано окно Properties (Свойства) после изменения свойства Text (Текст) первой надписи.



Рис. 6.13. Изменение значений свойств в окне Properties (Свойства)

5. Точно так же измените текст надписи 1аЬе12 на Balance (Баланс).
6. Введите значения свойств полей и кнопок в соответствии с табл. 6.1.
7. С помощью маркеров размера, которые находятся посредине каждой стороны формы, измените ее размер. При желании, перетащите управляющие элементы на выбранные места, и измените их размер. Если внешний вид формы вас удовлетворяет, сохраните изменения, сделанные в проекте. Ваша форма должна выглядеть примерно так, как на рис. 6.14.
8. Добавьте обработчики событий кнопок, дважды щелкнув на каждой кнопке.
9. Добавьте необходимый код к коду, сгенерированному мастером:
Таблица 6.1. Значения свойств полей (Textbox) и кнопок (Button)

Имя свойства Текст
txtAmount (не заполняется)
txtBalance (не заполняется)
cmdDeposit Deposit (Вклад)
cmdWithdraw Withdraw (Снять)



Рис. 6.14 Форма приложения BankGui

public class Forml : System.Windows.Forms.Form
// общедоступный класс Forml:Система.Windows.Формы.Форма
{
public Forml()
{
//
// Требуется для поддержки Windows Form Designer
//
InitializeComponent();
//
// TODO: Добавьте любой код конструктора после
// вызова InitializeComponent
//
txtAmount.Text = "25";
// Текст txtBalance.Text = "100";
// Текст
}
/// <summary>
/// The main entry point for the application.
/// Основная точка входа для приложения.
/// </summary>
[STAThread]
static void Main()
{
Application.Run(new Forml{));
}
private void cmdDeposit_Click(object sender,
System.EventArgs e)
{
int amount = Convert.Tolnt32(txtAmount.Text);
int balance = Convert.Tolnt32(txtBalance.Text); // баланс
balance += amount;
// баланс + = количество;
txtBalance.Text = Convert.ToString(balance); // Текст
}
private void cmdWithdraw_Click(object sender,
System.EventArgs e)
{
int amount = Convert.Tolnt32(txtAmount.Text);
int balance = Convert.Tolnt32(txtBalance.Text); // баланс
balance = amount;
txtBalance.Text = Convert.ToString(balance); // Текст
}

10. Откомпилируйте и выполните приложение. Оно должно вести себя как стандартное приложение Windows. Вы должны без проблем вносить деньги на счет и снимать деньги со счета. На рис. 6.15 показано выполняющееся приложение BankGui.



Рис. 6.15. Windows-приложение BankGui

В данный момент проект реализован на С#. И хотя этого, как правило, не делают, мы перенесем этот проект в C++ для того, чтобы показать, как это нужно делать. Сначала с помощью шаблона Managed C++ Empty Project (Пустой проект на управляемом C++) создадим новый проект C++, который назовем BankGuiPort.
Теперь создадим исходный файл Forml.cpp в проекте BankGuiPort и перенесем (с помощью команд сору (копировать) и paste (вставить)) код С# из исходного файла Forml. cs проекта BankGui.
Перенесите все строки кода из файла Forml.cs проекта BankGui в файл Forml.cpp проекта BankGuiPort. При таком переносе кода могут возникнуть проблемы и непредвиденные ситуации. Эти проблемы не будут рассмотрены в нашей книге и вам придется ознакомиться с ними самостоятельно, если вы и в дальнейшем захотите выполнять подобный перенос кода. Итак, откройте оба проекта — BankGui на С# и BankGuiPort на C++ — в двух копиях Visual Studio.NET и визуально сравните исходные файлы Forml.cs и Forml.cpp, чтобы получить представление о подробностях переноса кода.

//Form1.cpp
#using <mscorlib.dll>
#using <System.dll>
#using <System.Drawing.dll>
#using <Systern.Windows.Forms.dll>
using namespace System;
// использование пространства имен Система;
namespace BankGui
// пространство имен BankGui
{
_gc class Forml : public System::Windows::Forms::Form
// класс сборщика мусора Forml: общедоступная Система::
// Windows:: Формы:: Форма
{
private: // частный
System::Windows: Forms::Label *label1;
System::Windows: Forms::Label *labe!2;
System::Windows: Forms::TextBox *txtAmount;
System::Windows: Forms::TextBox *txtBalance;
System::Windows: Forms::Button *cmdDeposit; // Кнопка
System::Windows: Forms::Button *cmdWithdraw; // Кнопка
System::ComponentModel::Container *components; // Контейнер
public:
Form1 ()
{
components =0; // компоненты
InitializeComponent();
txtAmount->Text = "25"; // Текст
txtBalance->Text = "100"; // Текст
}
private: // частный
void InitializeComponent()
{
cmdWithdraw = new System: :Windows::Forms::Button;
// Кнопка
cmdDeposit = new System::Windows::Forms::Button;
// Кнопка
txtBalance = new System::Windows::Forms::TextBox;
txtAmount = new System::Windows::Forms::TextBox;
labell = new System::Windows::Forms::Label; // Надпись
Iabel2 = new System: : Windows :: Forms :-.Label; // Надпись
SuspendLayout();
//
// cmdWithdraw
//
cmdWithdraw->Location = // Местоположение
* _nogc new System::Drawing::Point(152, 144);
// Точка
cmdWithdraw->Name = "cmdWithdraw"; // Имя
cmdWithdraw->TabIndex = 2;
cmdWithdraw->Text = "Withdraw"; // Текст = "Снять"
cmdWithdraw->Click += // Щелчок
new System::EventHandler(this, cmdWithdraw_Click);
//
// Form1
//
AutoScaleBaseSize =
* _nogc new System::Drawing::Size(5, 13);
// Размер
ClientSize =
* _nogc new System::Drawing::Size(280, 189);
// Размер
System: :Windows::Forms::Control* pltems[] = {
cmdDeposit,
txtAmount,
label1,
label2,
txtBalance,
cmdWithdraw};
Controls->AddRange(pltems);
"Name = "Forml"; // Имя
Text = "Forml"; // Текст
Load += new System::EventHandler(this, Forml_Load);
ResumeLayout(false); // ложь } void Forml_Load(
Object *sender, System::EventArgs *e)
{
}
void cmdWithdraw_Click(
Object *sender, System::EventArgs *e)
{
int amount = Convert::ToInt32(txtAmount->Text);
// преобразование текста
int balance = Convert::ToInt32(txtBalance->Text);
// преобразование текста
balance -= amount;
// -количество
txtBalance->Text = Convert::ToString(balance);
// преобразование в текст }
public:
[STAThread] static void Main()
{
System::Windows::Forms::Application::Run(new Forml);
// Приложение:: Выполнить (новая Forml);
}
};
}


Окно конструктора (Design window) и окно кода (Code window)

Для работы с проектами Windows Forms (Формы Windows) в Visual Studio очень важно научиться переключаться между окном конструктора (Design window), где вы работаете с управляющими этементами на форме, и окном кода (Code window), где вы работаете с кодом программы Мы можем показать это на примере двух окон проекта VsForm на С#, код стартовой системы этого проекта находится в папке VsForm\Stepl главной папки данной главы Версия этого проекта, перенесенная из С# на C++, находится в папке VsFormPortXStepl Это первая версия проекта стартовой системы, которая отображает одну и ту же строку приветствия Проекты, отвечающие разным стадиям разработки, последовательно пронумерованы, и каждой версии проекта на С# (они содержатся в папке VsForm), созданной с помощью конструктора форм (Forms Designer), соответствует перенесенная версия проекта на C++, которая содержится в папке VsFormPort
Если дважды щелкнуть на файле Forml cs проекта VsForml\Stepl в окне Solution Explorer (Поиск решения), то файл будет открыт в окне конструктора (Design window), как на рис 6 16
Для того чтобы появилось окно кода (Code window), щелкните на кнопке View Code (Просмотреть код), находящейся на инструментальной панепи Таким образом вы откроете исходный код, и вверху главной области окна вы увидите горизонтально расположенные ярлыки, с помощью которых можно выбирать нужные окна В данный момент для этой формы открыты оба окна, — и окно конструктора (Design window), и окно кода (Code window) Можно без труда вернуться в окно конструктора (Design window), щелкнув на кнопке View Designer (Открыть окно Design), находящейся на инструментальной панели На рис 6 17 вы можете увидеть внешний вид окна кода (Code window)



Рис. 6.16. Окно конструктора (Design window) в проекте Windows Forms (Формы Windows)



Добавление события

1. Скомпонуйте и выполните программы (стартовые системы) на С# и C++, находящиеся в папках VsForm\Stepl и VsFormPortXStepl, и убедитесь, что они работают одинаково Это полностью статические приложения, — они просто отображают строку приветствия в фиксированной позиции
2. Откройте форму проекта VsForm\Stepl в окне конструктора (Design window) и щелкните на кнопке Events (События) в окне Properties (Свойства)
3. Найдите событие MouseDown (Кнопка мыши нажата), как на рис. 6.18.
4. В окне Properties (Свойства) дважды щелкните на событии MouseDown (Кнопка мыши нажата) Автоматически будет сгенерирован код, который зарегистрирует делегата для события и образует скелет метода, связанного с делегатом



Рис. 6.17. Окно кода (Code window) в проекте Windows Forms (Формы Windows)

private void InitializeComponent ()
{
this.MouseDown =
new System.WinForms.MouseEventHandler
(this.Forml_MouseDown);
}
protected void Forml_MouseDown (object sender,
System.WinForms.MouseEventArgs e)
{
}



Рис. 6.18. Добавление события с помощью кнопки Events (События)



Код обработчика события

1. Чтобы установить координаты строки приветствия, добавьте код в обработчик события мыши (нажатие кнопки мыши) Не забудьте после этого вызвать метод Invalidate (Считать недействительным)

protected void Forml_MouseDown (object sender,
System WinForms.MouseEventArgs e)
{
x = e X;
у = e Y;
Invalidate() ;
}

2. Скомпонуйте и выполните проект Теперь по щелчку мыши (любой кнопкой) приветствие должно перемещаться Проект сейчас находится на шаге 2 разработки и соответствует проекту, хранящемуся в папке VsForm\Step2
Вместо того, чтобы переносить каждую строчку кода, созданного на СП, в файл Forml cpp проекта VsForm\Step2, просто сделайте копию проекта VsFormPortXStepl, который уже получен с помощью переноса кода Потом перенесите несколько строчек кода, связанных с событием MouseDown (Кнопка мыши нажата) из VsForm\Step2

void InitializeComponent()
{
MouseDown +=
new System::Windows::Forms:-MouseEventHandler
(this, Forml_MouseDown);
}
void Forml_MouseDown (Object *sender,
System::Windows::Forms::MouseEventArgs *e)
{
x = (float)e->X; // с плавающей точкой
у = (float)e->Y; // с плавающей точкой
Invalidate();
}



Использование управляющего элемента Menu (Меню)

3. Откройте панель инструментов Toolbox, если она до сих пор еще не открыта (щелкните на ярлыке панели инструментов Toolbox в вертикальной линейке) и перетащите управляющий элемент MainMenu (Главное меню) на форму приложения.
4. Для создания выпадающего меню File (Файл) с пунктом Exit (Выход), введите File (Файл) и Exit (Выход), как на рис. 6.19.



Рис. 6.19. Использование управляющего элемента Menu (Меню) для добавления в форму меню

5. В окне Properties (Свойства) измените названия этих пунктов меню на menuFile и menuExit.
6. Дважды щелкните на Exit (Выход), чтобы добавить код в обработчик события File => Exit (Файл => Выход).
7. Добавьте в обработчик код, закрывающий приложение.

protected void menuExit_Click (object sender,
System.EventArgs e)
{
Application.Exit(); // Приложение.Выход
}

8. Скомпонуйте и выполните приложение. Меню должно полностью работать. Полученный проект соответствует проекту, который находится в папке VsForm\Step3.
И снова, вместо того, чтобы переносить каждую строчку кода, созданного на С#, из файла Forml.cpp проекта VsForm\step3 в проект на C++, просто сделайте копию проекта VsFormPort\Step2, созданного ранее с помощью переноса кода. Потом перенесите те несколько строчек кода, которые связаны с новыми функциями меню, из VsForm\Step3.

// VSForm - Step3
_gc class Forml : public System::Windows::Forms::Form
{
private: // частный
float x, у; // с плавающей точкой
Brush *pStdBrush; // Кисть
System: -.Windows: : Forms : :MainMenu *mainMenul;
System::Windows::Forms::MenuItem *menuFile;
System::Windows::Forms::MenuItem *menuExit;
private: // частный
void InitializeComponent()
{
menuFile =
new System: -.Windows: : Forms: :MenuItem() ; menuExit =
new System::Windows::Forms::MenuItem(); mainMenul =
new System::Windows::Forms::MainMenu() ;
//
// menuFile
//
menuFile->Index =0; // Индекс
System::Windows::Forms::MenuItem *pltems[] =
{menuExit};
menuFile->MenuItems->AddRange(pltems) ;
menuFile->Text = "File"; // menuFile-> Текст = "Файл";
//
// menuExit
//
menuExit->Index = 0;
// Индекс menuExit->Text = "Exit";
// menuExit-> Текст = "Выход";
menuExit->Click += // Щелчок
new System::EventHandler
(this, menuExit_Click);
//
// mainMenul
// System::Windows::Forms::MenuItem *pMenus[] =
{menuFile};
mainMenul->Menu!tems->AddRange(pMenus);
//
// Forml
// AutoScaleBaseSize =
* _nogc new System::Drawing::Size(5, 13);
// Размер ClientsTze =
* _nogc new System::Drawing::Size(248, 181);
// Размер Menu = mainMenul;
}
private: // частный
void menuExit_Click(
Object *sender, System::EventArgs *pe)
{
Application::Exit(); // Приложение:: Выход
}
};



Закрытие формы (Выход из формы)

Давайте сделаем так, что каждый раз, когда пользователь попытается закрыть приложение, приложение будет его спрашивать, действительно ли он хочет выйти Существует несколько способов закрыть окно-

щелкнуть на кнопке "X" (Закрыть) в правом верхнем углу окна; закрыть окно из системного меню в левом верхнем углу окна, закрыть окно с помощью комбинации клавиш AU+F4, выйти из приложения с помощью меню FileOExit (ФайлОВыход)

Когда закрывается форма, вызывается событие Closing (Процедура завершения) Можно остановить процедуру завершения, установив в обработчике этого события свойство Cancel (Отмена). (Сначала нужно, как обычно, добавить обработчик события Closing (Процедура завершения) ) Просто напечатайте код MessageBox (Окно сообщения), приведенный ниже

protected void Forml_Closing (object sender.
System.ComponentModel.CancelEventArgs e)
{
DialogResult status = MessageBox.Show( // состояние
"Do you want to close",
// "Хотите закрыть?",
"Simple Form (VS)", MessageBoxButtons.YesNo);
if (status == DialogResult.No)
// если (состояние == DialogResult. Нет)
{
e.Cancel = true; // Отмена = истина
}
}

Для того чтобы получить нужное нам поведение приложения, обработчик меню FileOExit (ФайлОВыход) должен не выходить из приложения, а закрыть главное окно, вызвав метод С lose (Закрыть)

protected void menuExit_Click (object sender,
System.EventArgs e)
{
//Application.Exit();
// Приложение.Выход ();
Close () ;
}

Теперь проект соответствует проекту, находящемуся в папке VSForm\Step4 Запустите программу, и попытайтесь закрыть окно приложения разными способами В любом случае должно появиться диалоговое окно, показанное на рис. 6.20.



Рис. 6.20. Диалоговое окно, которое спрашивает пользователя, действительно ли он хочет закрыть приложение

И, наконец, как обычно, перенесите код проекта, подготовленного на С#, в проект на C++ VSFormPort\Step4. Скомпонуйте и выполните программу на C++. Убедитесь в том, что она работает так же, как и программа на С#

// VSForm - Step4
_gc class Forml : public System:-.Windows :: Forms :: Form
{
private: // частный
void InitializeComponent()
{
Closing += // Закрытие
new CancelEventHandler(this, Forml_Closing) ;
}
void menuExit_Click(
Object *sender, System::EventArgs *pe)
{
//Application::Exit();
// Приложение:: Выход ()
; Close();
}
void Form1_Closing(
Object *sender, CancelEventArgs *e)
{
int status = MessageBox::Show( // состояние
"Do you want to close",
// "Хотите закрыть",
"Simple Form (VS)", // Простая Форма
MessageBoxButtons::YesNo);
if (status == DialogResult::No)
// если (состояние == DialogResult::No)
{
e->Cancel = true; // Отмена = истина
}
}
};

Все оставшиеся примеры в этой главе написаны исключительно на С#!

Далее в этой главе мы будем рассматривать только программы на С#, поскольку даже в программах, которые в основном написаны на C++, при разработке графических пользовательских интерфейсов, как правило, используется С#. Причина этого проста— в C++ не поддерживается конструктор форм (Forms Designer). Всегда помните о том, что вы можете сначала создать графический пользовательский интерфейс на С#, а потом, если понадобится, перенести этот код в C++. В предыдущих примерах мы показали, как это сделать.



Диалоговые окна

Использование диалоговых окон облегчает процесс взаимодействия пользователя с приложением Windows Диалоговое окно — это набор управляющих элементов, с помощью которых упрощается процесс ввода данных В предыдущем примере было описано, как создать простое диалоговое окно, которое позволяло бы пользователю на вопрос дать ответ "Да" или "Нет" Для создания таких диалоговых окон используется класс MessageBox (Окно сообщения) Более сложные диалоговые окна создаются на основе форм.
Мы проиллюстрируем создание диалоговых окон на примере графического пользовательского интерфейса для бюро путешествий Acme (Acme Travel Agency) Код примера, как обычно, находится в папке CaseStuay для этой главы Давайте рассмотрим простой диалог, с помощью которого можно добавить гостиницу в список гостиниц. Скомпонуйте и запустите пример В главной форме щелкните на кнопке Add... (Добавить ) После этого появится диалоговое окно New Hotel (Новая гостиница), которое показано на рис. 6.21.



Рис. 6.21. Диалоговое окно для добавления новой гостиницы

Теперь пользователь может вводить данные. Щелчок на кнопке ОК. приведет к тому, что информация будет запомнена Щелчок на кнопке Cancel (Отмена) приведет к тому, что данные будут проигнорированы. Это диалоговое окно, так же как и окно сообщения, является модальным Если модальное диалоговое окно открыто, то пользователь может взаимодействовать с приложением только через это окно, — ни с какими иными средствами приложения он взаимодействовать не может. Если вы попытаетесь сделать что-нибудь на главной форме, когда открыто диалоговое окно New Hotel (Новая гостиница), то услышите гудок. Существуют также и немодальные диалоговые окна, которые позволяют взаимодействовать с иными средствами приложения даже тогда, когда немодальное диалоговое окно открыто.
Если рассматривать диалоговое окно как форму, то обычно у него есть свои специфические характеристики. Как правило, у диалоговых окон нет системного меню, нет кнопок свертывания Minimize (Свернуть) и развертывания Maximize (Развернуть), причем размер окна фиксирован Вы можете наблюдать эти свойства на примере диалога New Hotel (Новая гостиница).
Для того чтобы продолжить испытание программы, введите данные о новой гостинице и щелкните на кнопке ОК. Программа вернет вас в главную форму, где новая гостиница будет отображена в списке гостиниц (рис. 6 22). На главной форме реализованы также и другие управляющие элементы графического интерфейса пользователя, например, окно, в котором отображается список гостиниц, а также многострочное текстовое поле для отображения текста, который не помещается в одну строку.



Рис. 6.22. Главная форма для управления гостиницами



Документация по диалогам .NET

Диалоговые окна очень хорошо описаны в документации по комплексу инструментальных средств разработки программ .NET Framework SDK Информацию по диалоговым окнам следует искать в подразделе "Dialog Boxes in Windows Forms" ("Диалоговые окна в Формах Windows") раздела "Introduction to Windows Forms" ("Введение в Формы Windows") Следует заметить, что во всех языках .NET принципы работы диалоговых окон одни и те же Это является серьезным отличием программирования на NET от традиционного программирования на Visual Basic и программирования с применением библиотеки базовых классов Microsoft (Microsoft Foundation Classes, MFC), где принципы создания диалоговых окон совершенно разные На рис. 6.23. показано, где искать документацию по диалоговым окнам



Рис. 6.23. Документация по диалоговым окнам в NET Framework



Демонстрация диалогового окна

Мы продемонстрируем детали реализации диалогового окна на примере создания диалога для изменения информации о гостинице в упрощенной версии нашего примера Код системы запуска нашего примера имеется в папке HotelAdminNStepl, которая находится в главной папке этой главы Окончательная версия программы находится в папке HotelAdmin\Step3 Можно запустить первую версию примера, а можно сразу запустить решение Step 3 и посмотреть, как должно выглядеть диалоговое окно в окончательной версии В главной форме выберите гостиницу, щелкнув на соответствующем элементе списка гостиниц Затем щелкните на кнопке Change (Изменить ) В результате появится диалоговое окно Change Hotel Information (Изменение информации о гостинице), показанное на рис. 6.24 Обратите внимание на то, что поля City (Город) и Hotel Name (Название гостиницы) недоступны Эти поля доступны только для чтения, и их значения изменить нельзя Пользователь может изменить только значения полей Rooms (Количество номеров) и Rate (Стоимость)

Создание модального диалога

В первой части нашей демонстрации мы научим вас создавать модальное диалоговое окно Мы покажем, как устанавливать свойства диалога и как возвращать результаты после щелчка на кнопке (Ж или Cancel (Отмена)
1. Скомпонуйте и запустите стартовую систему приложения Кнопки Add (Добавить ) и Delete (Удалить) работают, но для кнопки Change (Изменить ) имеется только заглушка, которая по щелчку на этой кнопке отображает пустую форму Это обычная форма Ее размер можно изменять, у нее есть системное меню, кнопки свертывания Minimize (Свернуть) и развертывания Maximize (Развернуть)
2. Откройте файл ChangeHotelDialog. cs в режиме Design (Конструктор) В окне Properties (Свойства) установите значение свойства FormBorderStyle равным FixedDialog
3. Установите значение свойств ControlBox, MinimizeBox и MaximizeBox равным False (Ложь) Сейчас можно скомпоновать и запустить приложение Теперь размер диалогового окна будет фиксированным, у него не будет системного меню, а в правом верхнем углу окна не будет кнопки "X", с помощью которой можно закрыть окно.



Рис. 6.24. Диалоговое окно для изменения информации о гостинице

4. Теперь необходимо определить надписи и текстовые поля, содержащие информацию о гостинице Кроме того, необходимо добавить кнопки ОК и Cancel (Отмена) Когда вы будете добавлять эти управляющие элементы, вы можете еще раз попрактиковаться в работе с панелью инструментов Toolbox (Инструментальная панель) Вы можете выбрать и другой подход скопировать эти свойства из файла NewHotelDialog.cs и затем вставить их, для этого нужно открыть оба файла в режиме Design (Конструктор)
5. Если вы использовали копирование и вставку, то у управляющих элементов свойства Name (Имя) и Text (Текст) уже определены правильно. В противном случае установите значения в соответствии с табл. 6.2.
6. Значение свойства Readonly для txtCity и txtHotelName установите равным true (истина).
7. Измените размер формы так, чтобы на ней помещались все добавленные управляющие элементы.
8. Установите значение свойства DialogResult кнопки ОК равным ОК. Точно так же установите значение этого свойства кнопки Cancel (Отмена) равным Cancel (Отмена). Сохраните изменения, сделанные в файле ChangeHotelDialog. cs.
9. В файле MainAdminForm.cs временно добавьте к обработчику cmdChange_Click код, который отвечает за отображение в текстовом поле Messages (Сообщения) строк "ОК" и "Cancel" ("Отмена") в зависимости оттого, как был закрыт диалог: с помощью кнопки ОК или Cancel (Отмена). Обратите внимание на то, что диалоговое окно отображается с помощью метода ShowDialog, а не метода Show (Показать), который используется для обычных форм. В качестве результата метод ShowDialog возвращает перечисление типа DialogResult.

private void cmdChange_Click(object sender,
System.EventArgs e)
{
ChangeHotelDialog dig = new ChangeHotelDialog();
DialogResult status = dig.ShowDialog(); // состояние
if (status == DialogResult.OK)
// если (состояние == DialogResult. OK)
{
txtMessages.Text = "OK"; // Текст
}
else
{
txtMessages.Text = "Cancel";
// txtMessages. Текст = "Отмена";
}
}

Таблица 6.2. Значения свойств текстовых полей и кнопок диалога ChangeHotelDialog.cs

Имя (Name) Текст
txtCity (не заполнено)
txtHotelName (не заполнено)
txtlMumberRooms (не заполнено)
txtRate (не заполнено)
cmdOk ОК
cmdCancel Cancel (Отмена)

10. Скомпонуйте и запустите пример. Теперь диалоговое окно уже можно открыть с помощью меню, а закрыть— с помощью любой из кнопок ОК или Cancel (Отмена), причем на экран будет выведено соответствующее сообщение. Можно проверить, что диалоговое окно является модальным, пощелкав мышью где-нибудь еще в приложении. Программа сейчас находится на шаге 2 разработки.

Передача информации между родительской формой и диалогом

Во второй части нашей демонстрации будет показано, как передавать информацию из родительской формы диалогу и как получать информацию от диалога. Для этих целей в классах .NET Framework нет встроенного механизма, но все-таки существует некоторый шаблон проектирования, которому можно следовать. В классе диалога для каждого сообщения (порции информации), которое может передаваться между родительской формой и диалогом, нужно определить некоторое свойство.
В нашем примере мы сделаем свойства City (Город) и HotelName (Название гостиницы) доступными только для записи, а свойства Rate (Стоимость) и NumberRooms — доступными для чтения и записи.
1. Для того чтобы реализовать указанные свойства, добавьте к классу Chan-geHotelDialog в файле ChangeHotelDialog. cs приведенный ниже код.

public string City
// общедоступный строковый - Город
{
set
{
txtCity.Text = value; // txtCity. Текст = значение;
}
}
public string HotelName // общедоступная строка HotelName
{
set
{
txtHotelName.Text = value; // txtHotelName. Текст = значение;
}
}
public int NumberRooms
{
get
{
return Convert.ToInt32 (txtNumberRooms.Text);
}
set
{
}
}
public decimal Rate // общедоступная десятичная Цена
{
get
{
return Convert.ToDecimal(txtRate.Text);
}
set
{
txtRate.Text = value.ToString(); // Текст = значение
}
}

2. Теперь, чтобы установить эти свойства перед вызовом диалогового окна, и для того, чтобы использовать эти свойства перед закрытием диалогового окна с помощью кнопки ОК., добавьте код в главную форму MainAdminForm.cs. Удалите или закомментируйте временно вставленный ранее код, который отображает строки "ОК." или "Cancel" ("Отмена") в поле Messages (Сообщения).

private void cmdChange_Click(object sender,
System.EventArgs e)
{
ChangeHotelDialog dig = new ChangeHotelDialog();
if (currHotel.HotelName != "")
{
dig.City = currHotel.City; // Город
dig.HotelName = currHotel.HotelName;
dig.NumberRooms = currHotel.NumberRooms; d
ig.Rate = currHotel.Rate;
}
else
{
MessageBox.Show("Please select a hotel",
// "Пожалуйста, выберите гостиницу ",
"Hotel Broker Administration",
MessageBoxButtons.OK,
MessageBoxIcon.Exclamation // Восклицательный знак
) ;
return;
}
DialogResult status = dig.ShowDialog(); // состояние
if (status == DialogResult.OK)
// если (состояние == DialogResult. OK)
{
string comment = hotelBroker.ChangeRooms( // строковый
// комментарий
currHotel.City, // Город
currHotel.HotelName,
dig.NumberRooms,
dig.Rate);
if (comment == "OK")
// если (комментарий == "OK")
{
ShowHotelList(hotelBroker.GetHotels(;) ;
txtMessages.Text = "Hotel " + currHotel.HotelName
// txtMessages. Текст = "Гостиница"
// + currHotel. HotelName
+ " has been changed";
// + " была изменена";
}
else
txtMessages.Text = comment; // Текст = комментарий
}
}

Структура currHotel содержит поля гостиницы, выбранной в данный момент в качестве элемента списка. В следующем разделе вы научитесь извлекать информацию из элемента списка и заполнять элемент списка.
3. Скомпонуйте и испытайте программу. Все функции диалога должны работать корректно. Полученный проект соответствует проекту HotelAdmin\Step3.


Управляющий элемент ListBox (Список элементов)

В .NET Framework поддерживается несколько управляющих элементов, с помощью которых можно отобразить списки элементов. Эти управляющие элементы позволяют пользователю выбрать элемент списка; обычно для выбора элемента требуется щелкнуть на нем. В этом разделе мы рассмотрим управляющий элемент ListBox (Список элементов).
Пример программы находится в папке HotelAdmm\Step3. Главная форма в файле MainAdminForm. cs содержит список элементов listHotels, в котором хранится список гостиниц. Каждая гостиница представлена в виде строки, а значения атрибутов гостиницы разделены запятыми.



Начальная загрузка списка элементов

При запуске программы HotelAdmin в процессе инициализации конструктор формы MainAdminForm осуществляет начальную загрузку списка элементов listHotels, — в него загружается список гостиниц.

public MainAdminForm()
{
//
// Требуется для поддержки конструктора форм Windows
// (Windows Form Designer)
//
InitializeComponent();
//
// TODO: Добавьте любой код конструктора после
// вызова InitializeComponent
//
hotelBroker = new HotelBroker();
ShowHotelList(hotelBroker.GetHotels()
};
}

Метод ShowHotelList отображает в списке элементов список массивов, в которых хранится информация о гостиницах. Чтобы получить список массивов, вызывается метод HotelBroker . GetHotels. Ниже приведен метод ShowHotelList.

private void ShowHotelList(ArrayList array) // массив
{
listHotels.Iterns.Clear();
if (array == null)
// если (массив == пустой указатель)
{
return; }

foreach(HotelListltem hotel in array) // гостиница в массиве
{
string city = hotel.City.Trim();
// строковый город = гостиница.Город.Вырезка();
string name = hotel.HotelName.Trim();
// строковое название = гостиница.HotelName.Вырезка();
string rooms = hotel.NumberRooms.ToString(); // гостиница
string rate = hotel.Rate.ToString();
string str = city + "," + name + ","
// строка str = город +,"" + название +,""
+ rooms + "," + rate;
listHotels.Items.Add(str); // Добавить элементы
}
}

Управляющий элемент ListBox (Список элементов) содержит свойство Items (Элементы), которое поддерживает коллекцию объектных ссылок. Сначала мы вызываем метод Items.Clear (Элементы.Очистить), чтобы очистить список элементов от тех элементов, которые отображаются в нем в момент вызова метода. Потом мы с помощью цикла перебираем гостиницы в списке массивов и создаем строку, которая состоит из полей структуры гостиницы, разделенных запятыми. Чтобы добавить эту строку в список элементов, вызывается метод Items .Add (Элементы.Добавить).



Выбор элемента в списке элементов ListBox

Чтобы в списке элементов выбрать какой-нибудь элемент, нужно щелкнуть на нем. Выбор элемента вызовет событие SelectedlndexChanged. Доступ к выбранному элементу можно получить с помощью свойств Selectedlndex и Selectedltem. Если никакой элемент не выбран, значение Selectedltem будет равно -1. Ниже приведен код обработчика события SelectedlndexChanged.

private void listHotels_Selected!ndexChanged(object sender,
System.EventArgs e) {
if (listHotels.Selectedlndex != -1)
{
string selected - (string) listHotels.Selectedltem;
// выбранная строка
char [ ] sep = new char[] {','};
// символ
string[] fields;
// строка [] поля;
fields = selected.Split(sep);
// поля = выбранное, разбить;
currHotel = new HotelListltem();
currHotel.City = fields[0];
// Город = поля [О];
currHotel.HotelName = fields[1];
// поля [1]
currHotel.NumberRooms = Convert.Tolnt32(fields[2]);
// поля [2]
currHotel.Rate = Convert.ToDecimal(fields[3]);
// поля [3]
}
else
{
currHotel.HotelName = "";
}
}

Поскольку в списке элементов элементы хранятся в виде объектных ссылок, мы выполняем приведение типа выбранного элемента к типу string (Строка). Для того чтобы выделить значения полей, разделенные запятыми, используется метод String. Split (Строка.Разбиение), затем значения сохраняются в массиве строк fields (поля). Из массива эти значения переносятся в currHotel, где они и хранятся. В предыдущем разделе мы использовали currHotel для инициализации диалоговых окон New Hotel (Новая гостиница) и Change Hotel Information (Изменение информации о гостинице).



в качестве структур данных для

Пример бюро путешествий Acme (Acme Travel Agency) был представлен в главе 4 "Объектно-ориентированное программирование на управляемом C++", где в качестве структур данных для хранения списков гостиниц, клиентов и резервирований мы использовали массивы. В главе 5 "Управляемый C++ в .NET Framework" мы внесли изменения в реализацию примера, и для хранения информации вместо массивов использовали коллекции. Мы также определили множество интерфейсов и передавали списки в качестве объектных ссылок ArrayList. В предыдущей главе был реализован интерфейс пользователя в виде командной строки. В папке CaseStudy этой главы реализован графический интерфейс пользователя, созданный с помощью Windows Forms (Формы Windows).
Вы уже видели главное окно приложения (рис. 6.22), которое ничем не отличается от главного окна упрощенного варианта программы HotelAdmin, служившей для демонстрации использования диалоговых окон. Кнопка Add... (Добавить...) позволяет добавлять новую гостиницу (рис. 6.21), а кнопка Change... (Изменить...) (рис. 6.24) позволяет изменять число номеров в гостинице и их цену. Кнопка Delete (Удалить) удаляет выбранную в настоящее время гостиницу.
По щелчку на кнопке Customers (Клиенты ) появляется форма Customer Management (Управление клиентами), на которой отображается список клиентов, зарегистрированных на текущий момент Клиента можно выбрать, щелкнув на его имени в списке На рис. 6.25 показана форма после того, как клиент был выбран.
В текстовом окне Id отображается идентификатор клиента Можно отменить регистрацию этого клиента, щелкнув на кнопке Unregister (Отменить регистрацию) Изменить адрес электронной почты клиента можно, щелкнув на кнопке Change Email (Изменить электронную почту (email)), — по щелчку появится диалоговое окно Щелкнув на кнопке One Customer (Этот клиент) можно сделать так, чтобы была отображена информация только об одном этом клиенте Если после этого щелкнуть на кнопке All Customers (Все клиенты), то в списке снова будут отображены все клиенты С помощью кнопки Register (Зарегистрировать) можно добавить нового клиента



Рис. 6.25. Форма управления клиентами Customer Management (Управление клиентами)

Третья основная форма нашего интерфейса пользователя — это форма HotelReserva-tions (Бронирование мест в гостинице), которая вызывается по щелчку на кнопке Reservations . (Резервирование ) главной управляющей формы Для того чтобы сделать резервирование, заполните поля Customer Id (Идентификатор клиента), Checkin Date (Дата прибытия), Number of Days (Количество дней) Поля City (Город) и HotelName (Название гостиницы) можно заполнить автоматически (при выборе гостиницы в списке элементов) И чтобы резервирование было выполнено, просто щелкните на кнопке Make Reservation (Сделать резервирование) Для того чтобы вывести список резервирований клиента по его идентификационному коду (Customer Id), необходимо щелкнуть на кнопке Show Reservations (Отобразить список резервирований) На рис. 6.26. показана эта форма после того, как клиент, идентификатор которого равен 1, сделал резервирование, и оно было отображено
Список резервировании можно очистить, щелкнув на кнопке Clear Reservations (Очистить список резервировании) Кнопка Cancel Reservation (Отмена резервирования) служит для того, чтобы отменить резервирование для клиента с определенным идентификационным кодом, который можно ввести вручную или выбрать в списке элементов Reservations (Резервирования)



Рис. 6.26. Форма Hotel Reservation для бронирования мест в гостинице

Пример бюро путешествии Acme (Acme Travel Agency) рассматривается также в последующих главах, и стоит поэкспериментировать с этим приложением уже сейчас Графический пользовательский интерфейс значительно удобнее, чем пользовательский интерфейс командной строки С другой стороны, в интерфейсе командной строки реализован простой глобальный блок try для цикла по всем командам, облегчающий обнаружение всех искчючений Но этот подход не годится для графических пользовательских интерфейсов В промышленных приложениях необходимо делать проверку исключений везде, где они могут произойти Наш пример упрощен в учебных целях, и мы не пытались тщательно обрабатывать все исключительные ситуации (исключения) Кроме того, мы допустили еще одно упрощение мы не проверяем, является ли идентификатор клиента (Customer ID), который используется при резервировании, кодом реапьно существующего, зарегистрированного клиента Эта проверка реализована в главе 9 "Программирование в ADO NET', где пример реализуется на основе базы данных


с помощью классов Windows Forms

В этой главе мы рассмотрели создание графических пользовательских интерфейсов с помощью классов Windows Forms (Формы Windows) из .NET Framework. Сперва мы рассмотрели основные принципы, и научились создавать с помощью .NET Framework простые Windows-приложения с самого начала, без использования каких-либо специальных средств. Рисование выполняется в переопределенном методе OnPaint с помощью шрифта и кисти. Для управления взаимодействием пользователя с программой используется механизм обработки событий .NET. Он применяется, например, для обработки событий мыши и клавиатуры. Управляющие элементы намного упрощают программирование Windows-приложений. Управляющий элемент меню упрощает процесс добавления меню к Windows-программе. Visual Studio.NET также намного упрощает процесс программирования Windows-приложений. К сожалению, в C++ не поддерживается конструктор форм (Forms Designer). Зато с помощью конструктора форм (Forms Designer) можно очень быстро создать проект на С#, который, при желании, можно перенести в проект на C++. Конструктор форм (Forms Designer) позволяет перетаскивать управляющие элементы с инструментальной панели (Toolbox) на форму и потом в режиме Design (Конструктор) определять свойства этих управляющих элементов. Можно также без труда добавить в приложение обработчики событий. Диалоговое окно — это особый вид формы, с помощью свойств которого можно реализовать обмен информацией между родительской формой и диалоговым окном. Управляющий элемент ListBox (Список элементов) упрощает процесс отображения списков. В заключение мы разработали графический пользовательский интерфейс для программы Acme Travel Agency (бюро путешествий Acme).